package main

import (
	"context"
	"fmt"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"gopkg.in/yaml.v3"
	"os"
)

func main() {

	var minioTool = MinioTool{bufferSize: 1024}
	minioTool.Open(os.Args[1], os.Args[2])
	defer minioTool.Close()

}

type Conf struct {
	Endpoint   string `yaml:"endpoint"`
	AccessKey  string `yaml:"access_key"`
	SecretKey  string `yaml:"secret_key"`
	Bucket     string `yaml:"bucket"`
	BufferSize int    `yaml:"buffer_size"`
}

type MinioTool struct {
	file       *os.File
	bufferSize int
	readCount  int
}

func (r *MinioTool) Open(dataFile string, confFile string) error {
	var err error
	r.file, err = os.Open(dataFile)

	if err != nil {
		fmt.Println(err)
		return err
	}

	conf, err := getConf(confFile)
	if err != nil {
		fmt.Println(err)
		return err
	}
	r.bufferSize = min(1024*1024*5, max(1024, conf.BufferSize))
	client, err := minio.New(conf.Endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(conf.AccessKey, conf.SecretKey, ""),
		Secure: false,
	})

	if err != nil {
		fmt.Println(err)
		return err
	}

	options := minio.PutObjectOptions{ContentType: "application/octet-stream"}
	// putObjectMultipartStreamNoLength
	uploadinfo, err := client.PutObject(context.TODO(), conf.Bucket, dataFile, r, -1, options)

	if err == nil {
		fmt.Printf("put object finished: %v, read times: %d, buffer size: %v\n", uploadinfo, r.readCount, r.bufferSize)
	} else {
		fmt.Printf("put object failed: %v, read times: %d, buffer size: %v, error: %v\n", uploadinfo, r.readCount, r.bufferSize, err)
	}
	return err
}

func getConf(fileName string) (*Conf, error) {

	file, err := os.Open(fileName)
	if err != nil {
		fmt.Printf("open config fail: %v", err)
		return nil, err
	}
	defer file.Close()

	decoder := yaml.NewDecoder(file)
	conf := &Conf{}
	err = decoder.Decode(conf)
	return conf, err
}

func (r *MinioTool) Read(data []byte) (int, error) {

	//return r.file.Read(data)
	// mock stream bytes piece by piece
	buf := make([]byte, r.bufferSize)
	rlen, err := r.file.Read(buf)
	r.readCount++
	return rlen, err
}

func (r *MinioTool) Close() error {
	if r.file != nil {
		err := r.file.Close()
		if err != nil {
			return err
		}
	}
	return nil
}
